package com.fintech.pangu.autoconfigure.security.oauth2.client;

import com.fintech.pangu.autoconfigure.security.properties.PanGuSecurityProperties;
import com.fintech.pangu.security.oauth2.sso.client.logout.OAuth2SsoClientAutoLogoutHandler;
import com.fintech.pangu.security.oauth2.sso.client.logout.OAuth2SsoClientAutoLogoutSuccessHandler;
import com.fintech.pangu.security.oauth2.sso.client.logout.OAuth2SsoClientNoticeLogoutSuccessHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;

import java.util.ArrayList;
import java.util.List;

/**
 * OAuth2 Client SSO自动登出子配置
 */
public class OAuth2SsoClientAutoLogoutConfigurer {

    private ApplicationContext applicationContext;

    /**
     * PanGu安全相关的所有配置
     */
    private PanGuSecurityProperties panGuSecurityProperties;

    /**
     * OAuth2 Client关于授权码流程相关配置
     */
    private OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;

    /**
     * 根据用户名查找session的仓库
     */
    private FindByIndexNameSessionRepository<? extends Session> sessionRepository;


    private OAuth2ProtectedResourceDetails getOAuth2ProtectedResourceDetails() {
        if(oAuth2ProtectedResourceDetails == null){
            synchronized (this){
                if(oAuth2ProtectedResourceDetails == null){
                    oAuth2ProtectedResourceDetails = this.applicationContext.getBean(OAuth2ProtectedResourceDetails.class);
                }
            }
        }
        return oAuth2ProtectedResourceDetails;
    }

    private FindByIndexNameSessionRepository<? extends Session> getSessionRepository() {
        if(sessionRepository == null){
            synchronized (this){
                if(sessionRepository == null){
                    sessionRepository = this.applicationContext.getBean(FindByIndexNameSessionRepository.class);
                }
            }
        }
        return sessionRepository;
    }


    /**
     * OAuth2 Client SSO自动登出子配置 构造方法
     * @param applicationContext
     * @param panGuSecurityProperties
     */
    public OAuth2SsoClientAutoLogoutConfigurer(ApplicationContext applicationContext, PanGuSecurityProperties panGuSecurityProperties) {
        this.applicationContext = applicationContext;
        this.panGuSecurityProperties = panGuSecurityProperties;
    }




    /**
     * OAuth2 SSO Client自动登出子配置入口
     * @param http
     * @throws Exception
     */
    public void configure(HttpSecurity http) throws Exception {
        http.logout()
                .logoutUrl("/logout")     // 普通logout
                .logoutSuccessHandler(oauth2SsoClientNoticeLogoutSuccessHandler()) // 通知 SSO Server logout
                .permitAll()
                .invalidateHttpSession(true)
            .and()
                // SSO Client自动登出Filter
                .addFilterBefore(ssoAutoLogoutFilter(), LogoutFilter.class);
    }


    /**
     * 自动登出处理器，通过base64解析后得到username，再找到对应session并清理
     */
    public LogoutHandler oauth2SsoClientAutoLogoutHandler() {
        OAuth2SsoClientAutoLogoutHandler oauth2SsoClientAutoLogoutHandler = new OAuth2SsoClientAutoLogoutHandler();
        oauth2SsoClientAutoLogoutHandler.setSessionRepository(getSessionRepository());
        return oauth2SsoClientAutoLogoutHandler;
    }

    /**
     * 用于SSO Server自动触发通知SSO Client登出场景，通过base64编码后的username登出
     * 端点： /ssoAutoLogout
     */
    public LogoutFilter ssoAutoLogoutFilter() {
        List<LogoutHandler> logoutHandlers = new ArrayList<>();
        logoutHandlers.add(oauth2SsoClientAutoLogoutHandler()); // 负责从base64编码取到username并清理session
        LogoutHandler[] handlers = logoutHandlers.toArray(new LogoutHandler[logoutHandlers.size()]);

        // 创建ssoAutoLogoutFilter，设置LogoutHandler 和 LogoutSuccessHandler
        LogoutFilter ssoAutoLogoutFilter = new LogoutFilter(new OAuth2SsoClientAutoLogoutSuccessHandler(), handlers);
        ssoAutoLogoutFilter.setLogoutRequestMatcher(new AntPathRequestMatcher("/ssoAutoLogout", "POST"));

        return ssoAutoLogoutFilter;
    }


    /**
     * 用于统一登出操作由SSO Client开始时，通知SSO Server登出
     * 在当前SSO Client登出操作后执行的LogoutSuccessHandler
     */
    @Bean
    public LogoutSuccessHandler oauth2SsoClientNoticeLogoutSuccessHandler() {
        OAuth2SsoClientNoticeLogoutSuccessHandler oauth2SsoClientNoticeLogoutSuccessHandler = new OAuth2SsoClientNoticeLogoutSuccessHandler();
        oauth2SsoClientNoticeLogoutSuccessHandler.setAuthServerLogoutUri(panGuSecurityProperties.getOauth2().getSsoClient().getAuthServerUri());
        oauth2SsoClientNoticeLogoutSuccessHandler.setOauth2ProtectedResourceDetails(getOAuth2ProtectedResourceDetails());
        return oauth2SsoClientNoticeLogoutSuccessHandler;
    }

}
